package com.kedacom.ctsp.authz.access;

import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.kedacom.ctsp.authz.Authorize;
import com.kedacom.ctsp.authz.entity.AuthResource;
import com.kedacom.ctsp.authz.entity.Authentication;
import com.kedacom.ctsp.authz.entity.ResourceEnum;
import com.kedacom.ctsp.authz.exception.NoPermissionAccessException;
import com.kedacom.ctsp.lang.mapper.BeanMapper;
import com.kedacom.ctsp.orm.param.Param;
import com.kedacom.ctsp.orm.param.Term;
import com.kedacom.ctsp.orm.param.TermEnum;
import com.kedacom.ctsp.orm.param.UpdateParam;
import com.kedacom.ctsp.web.entity.*;
import com.kedacom.ctsp.web.entity.param.QueryParamEntity;
import com.kedacom.ctsp.web.entity.param.UpdateParamEntity;
import com.kedacom.ctsp.web.vo.VO;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author xuwei
 */
public class DefaultDataAccessHandler extends DataAccessHandlerAdaptor implements DataAccessTermHandler {
    protected Logger logger = LoggerFactory.getLogger(this.getClass());

    private List<RecordableBeanHandler> dataScopeHandlers;
    private List<CustomDataAccessHandler> customDataAccessHandlers;


    @Autowired(required = false)
    public void setDataScopeHandlers(RecordableBeanHandler... dataScopeHandlers) {
        this.dataScopeHandlers = Arrays.asList(dataScopeHandlers);
    }

    @Autowired(required = false)
    public void setDataAccessCustomerHandler(CustomDataAccessHandler... customDataAccessHandlers) {
        this.customDataAccessHandlers = Arrays.asList(customDataAccessHandlers);
    }

    @Override
    protected boolean handleVo(Authentication auth, List<AuthResource> authResources, Authorize anno, VO target) {
        if (target instanceof RecordableBean) {
            dataScopeHandlers.stream().forEach(
                    h -> h.handle((RecordableBean) target, auth)
            );
        }
        return true;
    }

    @Override
    protected boolean handleQuery(Authentication auth, List<AuthResource> authResources, Authorize anno, Param target) {
        QueryParamEntity param = (QueryParamEntity) target;

        // 添加查询条件
        // 如: 旧的条件为 where name =? and sign = ?
        // 加上数据权限后为: where name = ? and sign = ? and (department_id in(?,?) or dept_id in (?,?))
        List<Term> terms = getDataScopeAndAccessTerms(authResources, auth, anno);
        if (CollectionUtils.isNotEmpty(terms)) {
            param.addTerms(terms);
        }
        return true;
    }

    private List<Term> getCustomDataAccessTerms(List<AuthResource> authResources, Authentication auth, Authorize anno) {
        List<Term> terms = Lists.newArrayList();
        if (CollectionUtils.isNotEmpty(customDataAccessHandlers)) {
            customDataAccessHandlers.stream().forEach(handler -> {
                Term term = handler.createTerm(authResources, auth, anno);
                terms.add(term);
            });
        }
        return terms;

    }

    @Override
    protected boolean handleUpdate(Authentication auth, List<AuthResource> authResources, Authorize anno, UpdateParam target) {
        UpdateParamEntity param = (UpdateParamEntity) target;

        if (param.getData() instanceof RecordableBean) {
            dataScopeHandlers.stream().forEach(
                    h -> h.handle((RecordableBean) target, auth)
            );
        }

        // 处理限制条件
        // 如: 旧的条件为 where name =? and sign = ?
        // 加上数据权限后为: where name = ? and sign = ? and (department_id in(?,?) or dept_id in (?,?))
        List<Term> terms = getDataScopeAndAccessTerms(authResources, auth, anno);
        param.addTerms(terms);
        return true;
    }

    /**
     * 拼装3种DataScope
     *
     * @param authResources
     * @return
     */
    @Override
    public List<Term> getDataScopeAndAccessTerms(List<AuthResource> authResources, Authentication auth, Authorize anno) {
        List<Term> terms = Lists.newArrayList();
        /**
         * 用户自身资源
         */
        Set<AuthResource> userResources = authResources.stream().filter(
                r -> r.getConfigSource().equals(ResourceEnum.SELF)
        ).collect(Collectors.toSet());

        setTermsDataAccess(userResources, auth, anno, terms);


        /**
         * 所分管的用户的资源权限
         */
        Set<AuthResource> userChargeResources = authResources.stream().filter(
                r -> r.getConfigSource().equals(ResourceEnum.USER_OF_CHARGE)
        ).collect(Collectors.toSet());
        setTermsDataAccess(userChargeResources, auth, anno, terms);


        /**
         * 所分管的部门的资源权限
         */
        Set<AuthResource> deptChargeResources = authResources.stream().filter(
                r -> r.getConfigSource().equals(ResourceEnum.DEPARTMENT_OF_CHARGE)
        ).collect(Collectors.toSet());
        setTermsDataAccess(deptChargeResources, auth, anno, terms);

        /**
         * 应用自定义的资源权限.
         */
        List<Term> customTerms = getCustomDataAccessTerms(authResources, auth, anno);
        if (CollectionUtils.isNotEmpty(customTerms)) {
            terms.addAll(customTerms);
        }

        return terms;
    }

    /**
     * 组装生成条件terms
     *
     * @param userResources
     * @param auth
     * @param terms
     */
    private void setTermsDataAccess(Set<AuthResource> userResources, Authentication auth, Authorize anno, List<Term> terms) {
        // 数据范围权限
        Term dsTerm = buildDataScopeTermByResource(userResources, auth, anno);
        // 自定义数据权限
        Term daTerm = buildDataAccessTermByResource(userResources);

        Term andTerm = joinTerm(dsTerm, daTerm);
        if (andTerm != null) {
            terms.add(andTerm);
        }
    }

    /**
     * 2个term组合在一起
     * and的形式
     *
     * @param dsTerm
     * @param daTerm
     * @return
     */
    private Term joinTerm(Term dsTerm, Term daTerm) {
        if (dsTerm == null) {
            if (daTerm == null) {
                return null;
            } else {
                return daTerm;
            }
        } else {
            if (daTerm == null) {
                return dsTerm;
            } else {
                dsTerm.addTerm(daTerm);
                return dsTerm;
            }
        }
    }


    /**
     * 组装自定义的数据权限
     *
     * @param userResources
     * @return
     */
    private Term buildDataAccessTermByResource(Set<AuthResource> userResources) {
        Term term = null;
        if (CollectionUtils.isNotEmpty(userResources)) {
            for (AuthResource ar : userResources) {
                // resource上获取配置的数据权限
                List<Term> dataAccess = ar.getDataAccess();
                if (CollectionUtils.isNotEmpty(dataAccess)) {
                    for (Term t : dataAccess) {
                        if (term == null) {
                            Term target = BeanMapper.newInstanceSilently(Term.class);
                            BeanMapper.deepCopy(t, target);
                            term = target;
                        } else {
                            // 全部是and关系
                            term.setType(Term.Type.and);
                            term.addTerm(t);
                        }
                    }
                }
            }
        }
        return term;
    }

    /**
     * 组装
     *
     * @param authResources
     * @return
     */
    private Term buildDataScopeTermByResource(Set<AuthResource> authResources, Authentication auth, Authorize anno) {
        if (CollectionUtils.isNotEmpty(authResources)) {
            // 数据访问权限和外面的是or关系

            // 获取当前最大权限
            DataAccessScopeEnum scope = DataAccessUtil.getDataScopeByResources(authResources);

            // 根据anno拼接条件
            // 可能有多个字段需要符合条件
            switch (scope) {
                default:
                case SELF:
                    return buildUserTerm(auth.getUser().getUsername(), auth.getPerson().getDeptCode(), anno == null ? null : anno.creatorColumn());
                case DEPARTMENT:
                    if (auth.getPerson() == null) {
                        throw new NoPermissionAccessException(anno.message());
                    }
                    return buildDepartmentTerm(auth.getPerson().getDeptCode(), anno == null ? null : anno.departmentColumn());
                case CASCADE:
                    if (auth.getPerson() == null) {
                        throw new NoPermissionAccessException(anno.message());
                    }
                    return buildDepartmentCascadeTerm(auth.getPerson().getCascadedAndChargedDeptCodes(), anno == null ? null : anno.departmentColumn());
                case ALL:
                    return null;
            }
        }
        return null;
    }

    /**
     * 自身权限
     *
     * @param currentUserCode
     * @param currentDeptCode
     * @param creatorColumns
     * @return
     */
    private Term buildUserTerm(String currentUserCode, String currentDeptCode, String[] creatorColumns) {
        //如果没有配置，默认取creator_code字段
        if (ArrayUtils.isEmpty(creatorColumns)) {
            return Term.build(CreatorRecordableBeanWithCode.CREATOR_CODE, TermEnum.eq, currentUserCode).//
                    and(CreateDeptNameRecordableBeanWithCode.CREATE_DEPARTMENT_CODE, TermEnum.eq, currentDeptCode);
        }
        // 如果定义了creatorColumn，取注解上配置的
        // 如果只有一个
        return buildTerm(currentUserCode, creatorColumns);
    }

    /**
     * 部门权限
     *
     * @param departmentCode
     * @param departmentColumns
     * @return
     */
    private Term buildDepartmentTerm(String departmentCode, String[] departmentColumns) {
        if (ArrayUtils.isEmpty(departmentColumns)) {
            return Term.build(CreateDeptRecordableBeanWithCode.CREATE_DEPARTMENT_CODE, TermEnum.eq, departmentCode);
        } else {
            return buildTerm(departmentCode, departmentColumns);
        }
    }

    private Term buildTerm(String value, String[] columns) {
        if (columns.length == 1) {
            return Term.build(columns[0], TermEnum.eq, value);
        } else {
            Term term = Term.build(columns[0], TermEnum.eq, value);
            for (int i = 1; i < columns.length; i++) {
                term.or(columns[i], TermEnum.eq, value);
            }
            return term;
        }
    }


    /**
     * 级联部门权限
     *
     * @param departmentCodes
     * @param departmentConditionsOfAnno
     * @return
     */
    private Term buildDepartmentCascadeTerm(Set<String> departmentCodes, String[] departmentConditionsOfAnno) {
        // 如果为空，则没有部门权限
        if (CollectionUtils.isEmpty(departmentCodes)) {
            return Term.build(CreateDeptRecordableBeanWithCode.CREATE_DEPARTMENT_CODE, TermEnum.in, Sets.newHashSet(0L));
        }
        //如果没有配置，默认取departmentId字段
        if (ArrayUtils.isEmpty(departmentConditionsOfAnno)) {
            return Term.build(CreateDeptRecordableBeanWithCode.CREATE_DEPARTMENT_CODE, TermEnum.in, departmentCodes);
        }
        // 如果定义了creatorColumn，取注解上配置的
        // 如果只有一个
        else if (departmentConditionsOfAnno.length == 1) {
            return Term.build(departmentConditionsOfAnno[0], TermEnum.in, departmentCodes);
        }
        // 如果有多个，用or连接
        // and (dept1 in (xx,xx) or (dpet2 in (xx,xx)))
        else {
            Term term = Term.build(departmentConditionsOfAnno[0], TermEnum.in, departmentCodes);
            for (int i = 1; i < departmentConditionsOfAnno.length; i++) {
                term.or(departmentConditionsOfAnno[i], TermEnum.in, departmentCodes);
            }
            return term;
        }
    }


}
